// #include <iostream>
#include "llvm/IR/Module.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Verifier.h"

/// .ll 结尾
/// .ll -> module -> {global*, function*}
/// function -> {basic block+}:至少有一个基本块,must have entry block

/// gen -> module -> main -> puts("hello world")

using namespace llvm;

// ; ModuleID = 'hello_world'
// source_filename = "hello_world"

// @gStr = private constant [11 x i8] c"hello,world"

// declare i32 @puts(ptr)

// define i32 @main() {
// entry:
//   %puts_call = call i32 @puts(ptr @gStr)
//   ret i32 0
// }

int main()
{
    /// 这是c++输出
    // std::cout << "hello world" << std::endl;

    /// 接下来是要用ir来输出
    /// 1.创建一个基本的模块
    llvm::LLVMContext context;
    llvm::Module m("hello_world", context);
    IRBuilder<> irBuilder(context);
    
    /// 5.构建int puts(char*)
    llvm::FunctionType *putsFuncType = llvm::FunctionType::get(irBuilder.getInt32Ty(), {irBuilder.getInt8PtrTy()}, false);
    llvm::Function *putsFunc = llvm::Function::Create(putsFuncType, llvm::GlobalVariable::LinkageTypes::ExternalLinkage, "puts", m);

    /// 2.构建一个全局变量“hello,world”
    llvm::Constant *c = ConstantDataArray::getString(context, "hello,world", false);
    llvm::GlobalVariable *gVar = new llvm::GlobalVariable(m, c->getType(), true, GlobalVariable::LinkageTypes::PrivateLinkage, c, "gStr");

    /// 3.构建main函数
    llvm::FunctionType *mainFuncType = llvm::FunctionType::get(irBuilder.getInt32Ty(), false);
    llvm::Function *mainFunc = llvm::Function::Create(mainFuncType, GlobalVariable::LinkageTypes::ExternalLinkage, "main", m);
    llvm::BasicBlock *entryBlock = BasicBlock::Create(context, "entry", mainFunc);
    irBuilder.SetInsertPoint(entryBlock);
    
    /// 4.获取全局变量的地址
    llvm::Value *gep = irBuilder.CreateGEP(gVar->getType(), gVar, {irBuilder.getInt64(0), irBuilder.getInt64(0)});

    /// 6.调用puts
    irBuilder.CreateCall(putsFunc, {gep}, "puts_call");

    irBuilder.CreateRet(irBuilder.getInt32(0));

    llvm::verifyFunction(*mainFunc, &errs());
    llvm::verifyModule(m, &errs());

    m.print(outs(), nullptr);

    return 0;
}